﻿using System.Runtime.CompilerServices;

namespace LxBasic
{
    public static class MultiThreadsV2
    {
        public static readonly object GlobalLock = new();

        private static readonly object taskLock = new();//全部用lock，避免重复调用
        private static Action<int, int> calTaskAct = (i, end) => { };
        //private volatile bool isContinue = true;
        private static Thread[] threads;
        private static int _ThreadsCount = 1;
        //private volatile int finishedCount = 0;
        //private volatile bool blockAt1Or2;

        //private ManualResetEventSlim hostResetEvent = new(false);
        //private ManualResetEventSlim calResetEvent1 = new(false);
        //private ManualResetEventSlim calResetEvent2 = new(false);
        //public int ThreadsCount { get => _ThreadsCount; }

        public static int ThreadCount
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get => _ThreadsCount;
            set => SetThreadsCount(value);
        }
        public static void SetThreadsCount(int count)
        {
            if (count < 1) count = 1;
            if (ThreadCount == count) return;
            lock (taskLock)
            {
                //if (threadsGroup != null)
                //{
                //    threadsGroup.Close();
                //}
                //threadsGroup = new ThreadGroup(count);
            }
        }
        public static void RunHostTask(Action hostAction)
        {
            Task.Run(hostAction);
        }
        private static (int start, int end) Split(IndexRange range, int threadId)
        {
            int nodeCount = range.GetCount();
            return (range.Start + nodeCount * threadId / ThreadCount, range.Start + nodeCount * (threadId + 1) / ThreadCount);
        }
        public static void ParallelRun(IndexRange totalRange, Action<int, int> subRangeAction)
        {
            lock (taskLock)
            {
                calTaskAct = subRangeAction;
                //set();
                (int i, int end) = Split(totalRange, 0);
                subRangeAction.Invoke(i, end);
                //wait0();
                calTaskAct = null;
            }
        }


        class ThreadGroup
        {
            public ThreadGroup(int threadCount)
            {
                _ThreadsCount = threadCount;
                threads = new Thread[_ThreadsCount - 1];
                for (int i1 = 1; i1 < _ThreadsCount; i1++)
                {
                    threads[i1 - 1] = new Thread(new ParameterizedThreadStart(calTask)) { IsBackground = true };
                    threads[i1 - 1].Start(new ThreadInfo(i1, _ThreadsCount));
                }
                wait0();
            }
            private volatile Action<ThreadInfo> calTaskAct = (i) => { };
            private volatile bool isContinue = true;
            private Thread[] threads;
            private volatile int _ThreadsCount = 1;
            private volatile int finishedCount = 0;
            private volatile bool blockAt1Or2;

            private ManualResetEventSlim hostResetEvent = new(false);
            private ManualResetEventSlim calResetEvent1 = new(false);
            private ManualResetEventSlim calResetEvent2 = new(false);
            public int ThreadsCount { get => _ThreadsCount; }
            public void Close()
            {
                if (isContinue)
                {
                    isContinue = false;
                    set();
                }
            }
            public void RunTask(Action<ThreadInfo> calTaskAct)
            {
                this.calTaskAct = calTaskAct;
                set();
                calTaskAct.Invoke(new ThreadInfo(0, _ThreadsCount));
                wait0();
                this.calTaskAct = null;
            }
            private void calTask(object? obj)
            {
#if DEBUG
                ThreadInfo threadInfo = (ThreadInfo)obj;
                while (isContinue)
                {
                    calTaskAct.Invoke(threadInfo);
                    wait1();
                }
#else
            try
            {
                ThreadInfo threadInfo = (ThreadInfo)obj;
                while (isContinue)
                {
                    calTaskAct.Invoke(threadInfo);
                    wait1();
                }
            }
            catch (Exception ex)
            {
                LxLog.Error(ex.Message);
                Environment.Exit(0);
            }
#endif
            }

            private void wait0()
            {
                if (Interlocked.Increment(ref finishedCount) == _ThreadsCount)
                {
                    return;
                }
                hostResetEvent.Wait();
            }
            private void wait1()
            {
                if (blockAt1Or2)
                {
                    if (Interlocked.Increment(ref finishedCount) == _ThreadsCount)
                    {
                        hostResetEvent.Set();
                    }
                    calResetEvent1.Wait();
                }
                else
                {
                    if (Interlocked.Increment(ref finishedCount) == _ThreadsCount)
                    {
                        hostResetEvent.Set();
                    }
                    calResetEvent2.Wait();
                }
            }
            private void set()
            {
                finishedCount = 0;
                hostResetEvent.Reset();
                if (blockAt1Or2)
                {
                    blockAt1Or2 = !blockAt1Or2;
                    calResetEvent2.Reset();
                    calResetEvent1.Set();
                }
                else
                {
                    blockAt1Or2 = !blockAt1Or2;
                    calResetEvent1.Reset();
                    calResetEvent2.Set();
                }
            }
            ~ThreadGroup()
            {
                Close();
            }
        }
    }
}
//public static void Sync(ThreadInfo threadInfo)
//{
//    threads.Sync(threadInfo);
//}
//public static void Sync(ThreadInfo threadInfo, Action runOnce)
//{
//    threads.Sync(threadInfo, runOnce);
//}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public static (int start, int end) Split(IndexRange indexRange, int threadId)
//{
//    int count = indexRange.GetCount();
//    return (indexRange.Start + count * threadId / _TaskCount, indexRange.Start + count * (threadId + 1) / _TaskCount);
//}
//public void Sync(ThreadInfo threadInfo)
//{
//    if (threadInfo == 0)
//    {
//        wait0();
//        set();
//    }
//    else
//    {
//        wait1();
//    }
//}
//public void Sync(ThreadInfo threadInfo, Action runOnce)
//{
//    if (threadInfo == 0)
//    {
//        wait0();
//        runOnce.Invoke();
//        set();
//    }
//    else
//    {
//        wait1();
//    }
//}